Skip to content

Add support for blend modes#97

Open
tychedelia wants to merge 4 commits intoprocessing:mainfrom
tychedelia:blend_modes
Open

Add support for blend modes#97
tychedelia wants to merge 4 commits intoprocessing:mainfrom
tychedelia:blend_modes

Conversation

@tychedelia
Copy link
Copy Markdown
Member

Blending and the GPU

If you open Photoshop and look at the layer panel, you can see a list of blending operations that are available to composite layers together:

![[Pasted image 20260328113313.png]]

This list is far longer than the blend modes [available in Processing](https://processing.org/reference/blendMode_.html), why?

This is because, in the context of a render pipeline, [blending or blend state](https://www.w3.org/TR/webgpu/#blend-state) is defined via [fixed function hardware](https://en.wikipedia.org/wiki/Fixed-function_(computer_graphics)) that defines how the GPU moves pixels into a color target (i.e. texture, framebuffer, etc.) after a fragment shader has run. More specifically, many useful compositing operations make use of division, which is [expensive for computers](https://en.wikipedia.org/wiki/Division_algorithm) and thus not implemented in fixed function graphics pipelines.

Alpha blending and render phases

In a traditional game engine like Bevy, objects in a 3d scene are drawn in the following order: first, all opaque items are drawn with depth buffer writes enabled. This allows for aggressive batching as items that are occluded by other objects will be rejected by the early Z test. In other words, the draws can be unordered. After this, transparent and transmissive materials that require alpha blending are drawn back to front, i.e. are sorted.

Typically, this means that transparent materials are substantially more expensive as, in the worst case, they may require constantly switching pipelines as items are drawn one by one.

With respect to blending, this means that most materials are either blended via "replace" (i.e. opaque, where one item fully occludes another) or alpha blending. Bevy's [AlphaMode](https://docs.rs/bevy/latest/bevy/prelude/enum.AlphaMode.html) supports other modes that are less frequently used for some materials that are typically on the VFX spectrum (fire, laser beams, etc).

Processing and batching

The default blend mode for Processing BLEND i.e. alpha blending. We adopt this same convention but are doing some additional magic behind the scene. Because libprocessing is built on top of Bevy, we aggressively take advantage of Bevy's built in material batching in order to reduce the number of draw calls we issue.

In the core render loop, in between each flush, we detect whether what the user is drawing actually requires alpha blending (i.e. whether the alpha channel is set) and only set the blend mode for items that do. Thus, from the perspective of the user, alpha blending "just works", but in our implementation if the user does not ever draw items that use alpha we will efficiently dispatch all their items in a single draw.

Recent [changed to Bevy](bevyengine/bevy#23005) now allow transparent items that share a render pipeline to be [drawn together](https://docs.vulkan.org/refpages/latest/refpages/source/VK_EXT_multi_draw.html). This is also helpful as it eliminates a major source of overhead

ProcessingMaterial

In order to facilitate changing arbitrary [BlendState](https://docs.rs/wgpu/latest/wgpu/struct.BlendState.html) beyond what Bevy supports via AlphaMode, we need to have a custom specialization function. As such, we now use an ExtendedMaterial that wraps StandardMaterial. We still use the default PBR shaders, so aren't customizing the material uniform at all here, but this allows us to override the PBR shader's blend mode.

We also perform a similar trick in custom material support.

@tychedelia tychedelia requested a review from catilac March 30, 2026 18:57
Copy link
Copy Markdown
Contributor

@catilac catilac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yay! I was wondering about the PyBlendMode naming. It looked like it was because of name clashes.


#[pyclass(name = "BlendMode", from_py_object)]
#[derive(Clone)]
pub struct PyBlendMode {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this named PyBlendMode do you think we want to move towards this schema?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants